﻿using System;
using DeviceSolutions.SPOT.Hardware;
using Gadgeteer;
using Microsoft.SPOT;
using Microsoft.SPOT.Hardware;
using Sytech.SPOT.Hardware.MeridianLcd;
using GT = Gadgeteer;

namespace Sytech.Gadgeteer
{
    /// <summary>
    /// Support class for Sytech Designs Ltd Nano for Microsoft .NET Gadgeteer
    /// </summary>
    public class Nano : GT.Mainboard
    {
        // The mainboard constructor gets called before anything else in Gadgeteer (module constructors, etc), 
        // so it can set up fields in Gadgeteer.dll specifying socket types supported, etc.

        /// <summary>
        /// Instantiates a new Meridian Nano mainboard
        /// </summary>
        public Nano()
        {
            // uncomment the following if you support NativeI2CWriteRead for faster DaisyLink performance
            // otherwise, the DaisyLink I2C interface will be supported in Gadgeteer.dll in managed code.
            GT.Socket.SocketInterfaces.NativeI2CWriteReadDelegate nativeI2C = null; // new GT.Socket.SocketInterfaces.NativeI2CWriteReadDelegate(NativeI2CWriteRead);

            // Connect the Native Bitmap convert Delegate
            NativeBitmapConverter = new BitmapConvertBPP(BitmapConverter);
           

            // For each socket on the mainboard, create, configure and register a Socket object with Gadgeteer.dll
            
            InitSocket1();
            InitSocket2();
            InitSocket3();
            InitSocket4();
            InitSocket5();
            InitSocket6();
            InitSocket7();
            InitSocket8();
            InitSocket9();
            InitSocket10();

             
             

        }

        /// <summary>
        /// Initialise Socket 1
        /// USB Device and I2C
        /// </summary>
        private void InitSocket1()
        {
             
            Socket socket;
            socket = GT.Socket.SocketInterfaces.CreateNumberedSocket(1);
            socket.SupportedTypes = new char[] { 'D', 'I' };
            socket.CpuPins[3] = Meridian.Pins.SSI_RXFS;
            socket.CpuPins[4] = (Cpu.Pin) SpecialPurposePin.USBD_DM;       // USB D-
            socket.CpuPins[5] = (Cpu.Pin)SpecialPurposePin.USBD_DP;       //USB D+
            socket.CpuPins[6] = Meridian.Pins.SSI_RXCLK;
            socket.CpuPins[7] = Meridian.Pins.SSI_RXDAT;
            socket.CpuPins[8] = Meridian.Pins.I2C_SDA;
            socket.CpuPins[9] = Meridian.Pins.I2C_SCL;
             
             
            GT.Socket.SocketInterfaces.RegisterSocket(socket);
        }

        /// <summary>
        /// Initialise Socket 2
        /// Com 1 Uart with Hanshaking
        /// 7 Gpio
        /// </summary>
        private void InitSocket2()
        {

            Socket socket;
            socket = GT.Socket.SocketInterfaces.CreateNumberedSocket(2);
            socket.SupportedTypes = new char[] { 'K', 'U' ,'Y'};
            socket.SerialPortName = "COM1";
            socket.CpuPins[3] = Meridian.Pins.SSI_TXCLK;
            socket.CpuPins[4] = Meridian.Pins.UART1_TXD;       // tx
            socket.CpuPins[5] = Meridian.Pins.UART1_RXD;       // rx
            socket.CpuPins[6] = Meridian.Pins.UART1_CTS;        // o/p is RTS - Meridian uart pin names are misleading
            socket.CpuPins[7] = Meridian.Pins.UART1_RTS;        // o/p is CTS - see above
            socket.CpuPins[8] = Meridian.Pins.SSI_TXFS;
            socket.CpuPins[9] = Meridian.Pins.SSI_TXDAT;
             

            GT.Socket.SocketInterfaces.RegisterSocket(socket);
        }

        /// <summary>
        /// Initialise Socket 3
        /// SPI
        /// 7 Gpio
        /// </summary>
        private void InitSocket3()
        {

            Socket socket;
            socket = GT.Socket.SocketInterfaces.CreateNumberedSocket(3);
            socket.SupportedTypes = new char[] { 'S','X' ,'Y' };
            socket.SPIModule = SPI.SPI_module.SPI1;
            socket.CpuPins[3] = Meridian.Pins.GPIO13;
            socket.CpuPins[4] = Meridian.Pins.TIN;            
            socket.CpuPins[5] = Meridian.Pins.SPI_RDY;        
            socket.CpuPins[6] = Meridian.Pins.SPI_SS;         
            socket.CpuPins[7] = Meridian.Pins.SPI_MOSI;        
            socket.CpuPins[8] = Meridian.Pins.SPI_MISO;
            socket.CpuPins[9] = Meridian.Pins.SPI_SCLK;


            GT.Socket.SocketInterfaces.RegisterSocket(socket);
        }

        /// <summary>
        /// Initialise Socket 4
        /// I2C
        /// 3 GPIO
        /// </summary>
        private void InitSocket4()
        {

            Socket socket;
            socket = GT.Socket.SocketInterfaces.CreateNumberedSocket(4);
            socket.SupportedTypes = new char[] { 'I', 'X' };
            socket.CpuPins[3] = Meridian.Pins.GPIO14;
            socket.CpuPins[4] = Meridian.Pins.TMR2OUT;
            socket.CpuPins[5] = Meridian.Pins.GPIO11;
            socket.CpuPins[6] = Meridian.Pins.GPIO10;
            socket.CpuPins[8] = Meridian.Pins.I2C_SDA;
            socket.CpuPins[9] = Meridian.Pins.I2C_SCL;


            GT.Socket.SocketInterfaces.RegisterSocket(socket);
        }

        /// <summary>
        /// Initialise Socket 5
        /// Serial with handshake ( COM2)
        /// 7 GPIO
        /// </summary>
        private void InitSocket5()
        {

            Socket socket;
            socket = GT.Socket.SocketInterfaces.CreateNumberedSocket(5);
            socket.SupportedTypes = new char[] { 'K','U' ,'X','Y' };
            socket.SerialPortName = "COM2";
            socket.CpuPins[3] = Meridian.Pins.GPIO6;
            socket.CpuPins[4] = Meridian.Pins.UART2_TXD;
            socket.CpuPins[5] = Meridian.Pins.UART2_RXD;
            socket.CpuPins[6] = Meridian.Pins.UART2_CTS;    //o/p is RTS, port labels misleading
            socket.CpuPins[7] = Meridian.Pins.UART2_RTS;    //o/p is CTS, port label misleading
            socket.CpuPins[8] = Meridian.Pins.GPIO7;
            socket.CpuPins[9] = Meridian.Pins.GPIO8;


            GT.Socket.SocketInterfaces.RegisterSocket(socket);
        }

        /// <summary>
        /// Initialise Socket 6
        /// Touch
        /// I2C
        /// </summary>
        private void InitSocket6()
        {

            Socket socket;
            socket = GT.Socket.SocketInterfaces.CreateNumberedSocket(6);
            socket.SupportedTypes = new char[] { 'I','T'   };
           
            socket.CpuPins[3] = Meridian.Pins.GPIO4;
            socket.CpuPins[4] = (Cpu.Pin)SpecialPurposePin.TOUCH_YU;      // Touch YU
            socket.CpuPins[5] = (Cpu.Pin)SpecialPurposePin.TOUCH_XL;      // Touch XL
            socket.CpuPins[6] = Meridian.Pins.GPIO1;        // Touch YD and GPIO1
            socket.CpuPins[7] = (Cpu.Pin)SpecialPurposePin.TOUCH_XR;      //Touch XR
            socket.CpuPins[8] = Meridian.Pins.I2C_SDA;
            socket.CpuPins[9] = Meridian.Pins.I2C_SCL;


            GT.Socket.SocketInterfaces.RegisterSocket(socket);
        }

        /// <summary>
        /// Initialise Socket 7
        /// Sytech Nano Specific Ethernet/SD socket
        /// SPI 
        /// 3 GPIO
        /// </summary>
        private void InitSocket7()
        {

            Socket socket;
            socket = GT.Socket.SocketInterfaces.CreateNumberedSocket(7);
            socket.SupportedTypes = new char[] { 'Z', 'S', 'X' };
            socket.SPIModule = SPI.SPI_module.SPI1;
            socket.CpuPins[3] = Meridian.Pins.GPIO15;       //SD Card SPI CS
            socket.CpuPins[4] = Meridian.Pins.GPIO12;       //SD Card Card Detect     
            socket.CpuPins[5] = Meridian.Pins.REV;          // Ethernet SPI CS
            socket.CpuPins[6] = Meridian.Pins.CLS;          // Ethernet INT    
            socket.CpuPins[7] = Meridian.Pins.SPI_MOSI;      
            socket.CpuPins[8] = Meridian.Pins.SPI_MISO;
            socket.CpuPins[9] = Meridian.Pins.SPI_SCLK;


            GT.Socket.SocketInterfaces.RegisterSocket(socket);
        }

        /// <summary>
        /// Initialise Socket 8
        /// LCD 3 ( BLUE)
        /// 7 GPIO
        /// Has Pixel Clock and LCD Enable
        /// </summary>
        private void InitSocket8()
        {

            Socket socket;
            socket = GT.Socket.SocketInterfaces.CreateNumberedSocket(8);
            socket.SupportedTypes = new char[] { 'B', 'X', 'Y' };
             
            socket.CpuPins[3] = Meridian.Pins.LCDD0;        
            socket.CpuPins[4] = Meridian.Pins.LCDD1;          
            socket.CpuPins[5] = Meridian.Pins.LCDD2;          
            socket.CpuPins[6] = Meridian.Pins.LCDD3;           
            socket.CpuPins[7] = Meridian.Pins.LCDD4;
            socket.CpuPins[8] = Meridian.Pins.ACD_OE;
            socket.CpuPins[9] = Meridian.Pins.LSCLK;


            GT.Socket.SocketInterfaces.RegisterSocket(socket);
        }

        /// <summary>
        /// Initialise Socket 9
        /// LCD 2 (GREEN)
        /// Also has Contrast
        /// 7 GPIO
        /// </summary>
        private void InitSocket9()
        {

            Socket socket;
            socket = GT.Socket.SocketInterfaces.CreateNumberedSocket(9);
            socket.SupportedTypes = new char[] { 'G', 'X', 'Y' };

            socket.CpuPins[3] = Meridian.Pins.LCDD5;
            socket.CpuPins[4] = Meridian.Pins.LCDD6;
            socket.CpuPins[5] = Meridian.Pins.LCDD7;
            socket.CpuPins[6] = Meridian.Pins.LCDD8;
            socket.CpuPins[7] = Meridian.Pins.LCDD9;
            socket.CpuPins[8] = Meridian.Pins.LCDD10;
            socket.CpuPins[9] = Meridian.Pins.CONTRAST;


            GT.Socket.SocketInterfaces.RegisterSocket(socket);
        }


        /// <summary>
        /// Initialise Socket 10
        /// LCD 1 (RED)
        /// Also has Vsync and Hsync
        /// 7 GPIO
        /// </summary>
        private void InitSocket10()
        {

            Socket socket;
            socket = GT.Socket.SocketInterfaces.CreateNumberedSocket(10);
            socket.SupportedTypes = new char[] { 'R', 'X', 'Y' };

            socket.CpuPins[3] = Meridian.Pins.LCDD11;
            socket.CpuPins[4] = Meridian.Pins.LCDD12;
            socket.CpuPins[5] = Meridian.Pins.LCDD13;
            socket.CpuPins[6] = Meridian.Pins.LCDD14;
            socket.CpuPins[7] = Meridian.Pins.LCDD15;
            socket.CpuPins[8] = Meridian.Pins.VSYNC;
            socket.CpuPins[9] = Meridian.Pins.HSYNC;


            GT.Socket.SocketInterfaces.RegisterSocket(socket);
        }


        bool NativeI2CWriteRead(int SocketNumber, GT.Socket.Pin sda, GT.Socket.Pin scl, byte address, byte[] write, int writeOffset, int writeLen, byte[] read, int readOffset, int readLen, out int numWritten, out int numRead)
        {
            // implement this method if you support NativeI2CWriteRead for faster DaisyLink performance
            // otherwise, the DaisyLink I2C interface will be supported in Gadgeteer.dll in managed code. 
            numRead = 0;
            numWritten = 0;
            return false;
        }

        /// <summary>
        /// Changes the programming interafces to the one specified
        /// Not supported, use the set up dip switches to set the 
        /// programming interface
        /// </summary>
        /// <param name="programmingInterface">The programming interface to use</param>
        public override void SetProgrammingMode(GT.Mainboard.ProgrammingInterface programmingInterface)
        {
            // Change the reflashing interface to the one specified, if possible.
            // This is an advanced API that we don't expect people to call much.
        }


        /// <summary>
        /// This sets the LCD configuration.  If the value GT.Mainboard.LCDConfiguration.HeadlessConfig (=null) is specified, no display support should be active.
        /// If a non-null value is specified but the property LCDControllerEnabled is false, the LCD controller should be disabled if present,
        /// though the Bitmap width/height for WPF should be modified to the Width and Height parameters.
        /// </summary>
        /// <param name="lcdConfig">The LCD Configuration</param>
        public override void SetLCD(GT.Mainboard.LCDConfiguration lcdConfig)
        {
            if (lcdConfig == null)
            {
                // headless mode disabling LCD Controller not supported yet
                return;
            }

            SPOT.Hardware.MeridianLcd.LCDConfiguration lcdConfiguration = new SPOT.Hardware.MeridianLcd.LCDConfiguration();

            if (!CheckLCDConfigChanged(lcdConfiguration, lcdConfig)) return;
            lcdConfiguration.Height = (int)lcdConfig.Height;
            lcdConfiguration.Width = (int)lcdConfig.Width;

            lcdConfiguration.HSyncWidth = lcdConfig.HorizontalSyncPulseWidth;
            lcdConfiguration.HSyncWait1 = lcdConfig.HorizontalFrontPorch;
            lcdConfiguration.HSyncWait2 = lcdConfig.HorizontalBackPorch;
            lcdConfiguration.HsyncPolarity = lcdConfig.HorizontalSyncPolarity
                                                 ? LcdPolarity.ActiveHigh
                                                 : LcdPolarity.ActiveLow;


            lcdConfiguration.VSyncWidth = lcdConfig.VerticalSyncPulseWidth;
            lcdConfiguration.VSyncWait1 = lcdConfig.VerticalFrontPorch;
            lcdConfiguration.VSyncWait2 = lcdConfig.VerticalBackPorch;
            lcdConfiguration.VsyncPolarity = lcdConfig.VerticalSyncPolarity
                                                 ? LcdPolarity.ActiveHigh
                                                 : LcdPolarity.ActiveLow;

            lcdConfiguration.OutputEnablePolarity = lcdConfig.OutputEnablePolarity
                                                        ? LcdPolarity.ActiveHigh
                                                        : LcdPolarity.ActiveLow;
            lcdConfiguration.PixelPolarity = lcdConfig.PixelPolarity
                                                 ? LcdPolarity.ActiveHigh
                                                 : LcdPolarity.ActiveLow;
            lcdConfiguration.PixelClockDivider = lcdConfig.PixelClockDivider;

            // work around to ensure shift clcok edge correct
            LCDShiftPolarityCheck(lcdConfiguration);

            lcdConfiguration.WriteConfiguration();
        }

        /// <summary>
        /// Om a Meridian 4.3 LCDs generally require the shift clcok polarity to be active low,
        /// but on 3.5 LCDs it is active high. Current api does not have a var for
        /// shift clock polarity ( clock edge) so we have this work around to set the
        /// correct polarity - allows switching between GHI 3.5 lcd and Sytech 4.3
        /// 
        /// Allows for display to be rotated
        /// </summary>
        /// <param name="lcdConfiguration"></param>
        private void LCDShiftPolarityCheck(SPOT.Hardware.MeridianLcd.LCDConfiguration lcdConfiguration)
        {
            if ((lcdConfiguration.Width == 480 && lcdConfiguration.Height == 272) ||
                (lcdConfiguration.Width == 272 && lcdConfiguration.Height == 480))
            {
                //check shift clock polarity is active low
                if (lcdConfiguration.LcdShiftClockPolarity == LcdPolarity.ActiveHigh)
                {
                    lcdConfiguration.LcdShiftClockPolarity = LcdPolarity.ActiveLow;
                }
            }
            else if ((lcdConfiguration.Width == 320 && lcdConfiguration.Height == 240) ||
                    (lcdConfiguration.Width == 240 && lcdConfiguration.Height == 320))
            {
                // check shift clock polarity is active high
                if (lcdConfiguration.LcdShiftClockPolarity == LcdPolarity.ActiveLow)
                {
                    lcdConfiguration.LcdShiftClockPolarity = LcdPolarity.ActiveHigh;
                }
                
            }
            
        }


        /// <summary>
        /// Check the existing configuration from flash agains the new configuration.
        /// Return true if there are any changes
        /// False if they are the same
        /// </summary>
        /// <param name="lcdConfiguration"></param>
        /// <param name="lcdConfig"></param>
        /// <returns></returns>
        private bool CheckLCDConfigChanged(SPOT.Hardware.MeridianLcd.LCDConfiguration lcdConfiguration, LCDConfiguration lcdConfig)
        {
            // work round for GHI 3.5 settings
            //if GHI 3.5 Vsync width needs to be incremented by 1 clcok pulse
            if (((lcdConfig.Width == 320 && lcdConfig.Height == 240) ||
                    (lcdConfig.Width == 240 && lcdConfig.Height == 320)) &&
                    lcdConfig.VerticalSyncPulseWidth==10)
            {
                lcdConfig.VerticalSyncPulseWidth = 11;
            }

                LcdPolarity hsyncPolarity = lcdConfig.HorizontalSyncPolarity ? LcdPolarity.ActiveHigh : LcdPolarity.ActiveLow;
                LcdPolarity vsyncPolarity = lcdConfig.VerticalSyncPolarity ? LcdPolarity.ActiveHigh : LcdPolarity.ActiveLow;
                LcdPolarity pixelPolarity = lcdConfig.PixelPolarity ? LcdPolarity.ActiveHigh : LcdPolarity.ActiveLow;
                LcdPolarity oePolarity = lcdConfig.OutputEnablePolarity ? LcdPolarity.ActiveHigh : LcdPolarity.ActiveLow;
            bool hasChange =
                lcdConfiguration.Width != lcdConfig.Width ||
                lcdConfiguration.Height != lcdConfig.Height ||
                lcdConfiguration.HSyncWidth != lcdConfig.HorizontalSyncPulseWidth ||
                lcdConfiguration.HSyncWait1 != lcdConfig.HorizontalFrontPorch ||
                lcdConfiguration.HSyncWait2 != lcdConfig.HorizontalBackPorch ||
                lcdConfiguration.VSyncWidth != lcdConfig.VerticalSyncPulseWidth ||
                lcdConfiguration.VSyncWait1 != lcdConfig.VerticalFrontPorch ||
                lcdConfiguration.VSyncWait2 != lcdConfig.VerticalBackPorch ||
                lcdConfiguration.PixelClockDivider != lcdConfig.PixelClockDivider ||
                lcdConfiguration.HsyncPolarity != hsyncPolarity ||
                lcdConfiguration.VsyncPolarity != vsyncPolarity ||
                lcdConfiguration.PixelPolarity != pixelPolarity ||
                lcdConfiguration.OutputEnablePolarity != oePolarity;



            return hasChange;


        }

        // change the below to the debug led pin on this mainboard
        private const Cpu.Pin DebugLedPin = Meridian.Pins.PWM;

        private Microsoft.SPOT.Hardware.OutputPort debugled = new OutputPort(DebugLedPin, false);
        /// <summary>
        /// Turns the debug LED on or off
        /// </summary>
        /// <param name="on">True if the debug LED should be on</param>
        public override void SetDebugLED(bool on)
        {
            debugled.Write(on);
        }

        /// <summary>
        /// This performs post-initialization tasks for the mainboard.  It is called by Gadgeteer.Program.Run and does not need to be called manually.
        /// </summary>
        public override void PostInit()
        {
            return;
        }

        /// <summary>
        /// The mainboard name, which is printed at startup in the debug window
        /// </summary>
        public override string MainboardName
        {
            get { return "Sytech Designs Ltd Nano"; }
        }

        /// <summary>
        /// The mainboard version, which is printed at startup in the debug window
        /// </summary>
        public override string MainboardVersion
        {
            get { return "1.0"; }
        }

        /// <summary>
        /// Native Bitmap convert Utility
        /// </summary>
        /// <param name="bitmapBytes"></param>
        /// <param name="pixelBytes"></param>
        /// <param name="bpp"></param>
        void BitmapConverter(byte[] bitmapBytes, byte[] pixelBytes,GT.Mainboard.BPP bpp)
        {
            if ( bpp != GT.Mainboard.BPP.BPP16_BGR_BE) throw new ArgumentOutOfRangeException("bpp", "Only BPP16_BGR_LE supported");

            uint pixelsConverted = GraphicsUtility.Convert(bitmapBytes, pixelBytes);
        }

    }

    enum SpecialPurposePin
    {
        // cant use -1 = unspecified pin 0xffff
        TOUCH_XL = -2,
        TOUCH_XR = -3,
        TOUCH_YU = -4,
        TOUCH_YD = -5,

        USBD_DM = -6,
        USBD_DP = -7

        
    }



    


    


    
}
